/****************************************************************************
*																			*
*  Lister -- by Keith Rollin												*
*																			*
*																			*
*	19-MAR-88	v1.0B1	Kaar	New Today									*
*	20-MAR-88	v1.0B2	Kaar	Fixed bug where About zooming would draw	*
*								with a grey outline. Fixed mysterious		*
*								disabling of About button.					*
*	28-MAR-88	v1.0B3	Kaar	Added support for PicComments. Added		*
*								Command codes for Print and File Buttons.	*
*	10-MAY-88   v1.0B4  Kaar    Allocated 2 pages for print manager (like I	*
*								should have). This fixed a problem with the	*
*								Font Manager not working.      				*
*	11-MAY-88	v1.0B5	Kaar	Added more support for different fonts when	*
*								using postscript. Tried to fix problems		*
*								Command occured when changing types of		*
*								printers, but I failed.						*
*	26-JUN-88	v1.0B6	Kaar	Put the whole program into Standard Format.	*
*								Added comments and error checking. Added	*
*								code to preserve settings when we change	*
*								printers. Worked around ChooseFont bug.		*
*	27-JUN-88	v1.0B7	Kaar	Cleaned up the way the Percent Reduction	*
*								box was handled. Changed the way I set the	*
*								font in the LaserWriter. Changed the key	*
*								equivalent of Quit from <return> to Cmd-Q.	*
*	 3-JUL-88	v1.0B8	Kaar	Finally found the source of a couple of		*
*								Mysterious Bugs. One caused the font in the *
*								main window to change, and the other caused *
*								the appearance of printing, when nothing	*
*								really happened. Both of these were due to	*
*								the Print Manager short-circuiting some		*
*								routines when errors occurred when it		*
*								shouldn't have. They've been worked around	*
*								and only occur on System Disk 3.2; they've	*
*								been fixed in System Disk 4.0.				*
*    1-SEP-88   v1.0    Kaar	First Release.								*
*    7-DEC-88   v1.0.1  Mensch	Removed desktop call.						*
*																			*
*																			*
****************************************************************************/

/*********************************************************************
*                                                                    *
*             Apple IIGS Source Code Sampler, Volume I               *
*                                                                    *
*             Copyright (c) Apple Computer, Inc. 1988                *
*                       All Rights Reserved                          *
*                                                                    *
*            Written by Apple II Developer Tech Support              *
*                                                                    *
*                                                                    *
*                                                                    *
*  ----------------------------------------------------------------  *
*                                                                    *
*     This program and its derivatives are licensed only for         *
*     use on Apple computers.                                        *
*                                                                    *
*     Works based on this program must contain and                   *
*     conspicuously display this notice.                             *
*                                                                    *
*     This software is provided for your evaluation and to           *
*     assist you in developing software for the Apple IIGS           *
*     computer.                                                      *
*                                                                    *                                                                    *
*     DISCLAIMER OF WARRANTY                                         *
*                                                                    *
*     THE SOFTWARE IS PROVIDED "AS IS" WITHOUT                       *
*     WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED,               *
*     WITH RESPECT TO ITS MERCHANTABILITY OR ITS FITNESS             *
*     FOR ANY PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO             *
*     THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH            *
*     YOU.  SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU (AND            *
*     NOT APPLE OR AN APPLE AUTHORIZED REPRESENTATIVE)               *
*     ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING,             *
*     REPAIR OR CORRECTION.                                          *
*                                                                    *
*     Apple does not warrant that the functions                      *
*     contained in the Software will meet your requirements          *
*     or that the operation of the Software will be                  *
*     uninterrupted or error free or that defects in the             *
*     Software will be corrected.                                    *
*                                                                    *
*     SOME STATES DO NOT ALLOW THE EXCLUSION                         *
*     OF IMPLIED WARRANTIES, SO THE ABOVE EXCLUSION MAY              *
*     NOT APPLY TO YOU.  THIS WARRANTY GIVES YOU SPECIFIC            *
*     LEGAL RIGHTS AND YOU MAY ALSO HAVE OTHER RIGHTS                *
*     WHICH VARY FROM STATE TO STATE.                                *
*                                                                    *
*                                                                    *
*********************************************************************/
	


#define  dtItemListLength  22   /* Number of Dialog Items */

#include <types.h>
#include <prodos.h>
#include <misctool.h>
#include <quickdraw.h>
#include <window.h>
#include <memory.h>
#include <dialog.h>
#include <menu.h>
#include <control.h>
#include <desk.h>
#include <event.h>
#include <lineedit.h>
#include <misctool.h>
#include <locator.h>
#include <Stdfile.h>
#include <qdaux.h>
#include <print.h>
#include <font.h>
#include <intmath.h>
#include <scrap.h>
#include <texttool.h>

extern pascal Word AlertWindow() inline(0x590E,dispatcher);

/* ************************************************************************* */
/*																			 */
/* Standard Global data here												 */
/*																			 */
/* ************************************************************************* */

char TitleString[]	= "\Text File Lister";
char AutString[]	= "\pBy Keith Rollin ©1988 Apple Computer";
char VersString[]	= "\pVersion: 1.0";

extern int _toolErr;

int MyID;
#define ThisMode mode640			/* Init mode = 640						*/
#define ScreenWidth 640				/* Used to init the Event Manager		*/
#define MAXSCAN    320				/* Used to init QuickDraw. NOTE: As per */
									/* the Printing Manager chapter, this   */
									/* value is twice as large as you would */
									/* normally expect it to be. This is to */
									/* take into account double resolution  */
									/* when printing in Better Text mode on */
									/* the ImageWriter.						*/

int ToolTable[] = {11,
                14, 0x0100,			/* Window Manager	*/
                15, 0x0100,			/* Menu Manager		*/
                16, 0x0100,			/* Control Mnager	*/
                20, 0x0100,			/* Line Edit		*/
                21, 0x0100,			/* Dialog Manager	*/
                22, 0x0100,			/* Scrap Manager	*/
				18, 0x0100,			/* QD Aux			*/
				19, 0x0100,			/* Print Manager	*/
				23, 0x0100,			/* Standard File	*/
				27, 0x0100,			/* Font Manager		*/
				28, 0x0100			/* List manager		*/
				};


/* ************************************************************************* */
/*																			 */
/* Application specific data appears here									 */
/*																			 */
/* ************************************************************************* */

/* The LaserWriter Driver on System Disk 3.2 supports PicComments, commands	*/
/* that are included with QDAux pictures and that customize the appearance	*/
/* of pictures. The LaserWriter Driver intercepts the routine that handles	*/
/* PicComments, and can be commanded to perform special actions that are	*/
/* not normally available to QuickDraw. In this case, we are going to use	*/
/* a facility to send raw PostScript commands to the LaserWriter driver. We	*/
/* do this by puttin the LaserWriter driver into 'PostScript Mode' with the	*/
/* PostScriptBegin PicComment . The we describe HOW we are going to send	*/
/* the PostScript text. In this program, we want to send the commands with	*/
/* the DrawString and DrawCString commands. We tell the LaserWriter driver	*/
/* of our intentions with the TextIsPostScript PicComment. Finally, when we	*/
/* are done, we issue the PostScriptEnd PicComment to put the LaserWriter	*/
/* driver back into normal QuickDraw mode. The definitions below facilitate	*/
/* all of this.																*/

#define PostScriptBegin		190
#define TextIsPostScript	194
#define PostScriptEnd		191

/* Forward declarations of some routines */

extern void SetReduction();
extern void SetOrientation();
extern void SetVSizing();
extern void CreatePSSetFontCommand();
extern pascal void DrawUserItem();

char		**y,*z;			/* ZPHandle and ZPPointer						*/
SFReplyRec	myReply;		/* Reply record for SFGetFile					*/
GrafPortPtr myDialog;		/* Dialog box record pointer					*/
PrRecHndl	PrintHndl;		/* Handle to my Print record					*/
int			numCols;		/* Number of columns per page to print			*/
int			theReduction;	/* Amount of reduction I'll be doing			*/
int			theVSizing;		/* Vertical aspact ratio						*/
LongWord	theFont;		/* The ID of the font to print in				*/
boolean		sideways;		/* flag for Landscape or Portrait printing		*/
boolean		PostScript;		/* TRUE if printing to PostScript device.		*/

int			lineHeight,		/* variables used for page layout when printing */
			maxLines,
			lineTop,
			lineBottom,
			pageNum;
boolean		haveHeader,		/* These too									*/
			haveFooter;
char		line[255];		/* buffer for current line to print				*/
Rect		myPage,			/* used to change the size of page to print to	*/
			*myPagePtr;
char		time[20];		/* string to hold the time when printing		*/


/* ProDOS records for I/O */

OpenRec 	OpenParms 		= {0,myReply.fullPathname,0};
NewlineRec 	NewlineParms 	= {0,0x7f,0x0d};
FileIORec 	IOParms 		= {0,line,255,0};


/* Record for SFGetFile. It specifies the types of files to display in the	*/
/* dialog box. The first byte indicates the number of filetypes we will		*/
/* allow to be displayed in the dialog box. The subsequent bytes are the	*/
/* filetypes. In this case, we want to read in one of two type: TXT and SRC.*/

byte myTypes[] = {2,4,176};		/* TXT = 4, SRC = 176	*/


/* String constants. */
/* The first is used by SFGetFile. 											*/

char prompt[] = "\pLoad what file:";

/* Used to create PostScript commands that position and print the string.	*/

char PSMoveStr[] = " moveto\r";
char PSShow1[] = " (";
char PSShow2[] = ") show\r";

/* Used for AlertWindow Dialogs												*/

char NoMemAlertString[] = "32/Not enough memory to print the file!!!/^#0";
char ErrCanceled[] = "32/Printing was canceled because you pressed \021-./^#0";
char ErrMissingDriver[] = "32/Specified driver not in the DRIVERS subdirectory of the SYSTEM subdirectory/^#0";
char ErrPortNotOn[] = "32/Specified port not selected in the Control Panel./^#0";
char ErrNoPrintRecord[] = "32/No print record was specified./^#0";
char ErrBadLaserPrep[] = "42/The version of the LaserPrep file in the LaserWriter is not compatible with this version of the Print Manager/^#0";
char ErrBadLPFile[] = "42/The version of the LaserPrep file in the DRIVERS subdirectory in the SYSTEM subdirectory is not compatible with this verson of the Print Manager/^#0";
char ErrPapConnNotOpen[] = "32/Connection couldn't be established with the LaserWriter./^#0";
char ErrPapReadWriteErr[] = "32/Read-Write error on the LaserWriter./^#0";
char ErrPntrConFailed[] =  "32/Connection to the printer failed./^#0";
char ErrGeneric[] = "32/Print error $*0 occured!!!/^#0";
char PrintErrNum[] = "xxxx";
Pointer SubStrings[] = {
		PrintErrNum
		};



/* ID numbers of the Dialog items */

#define Quit			22
#define ChFile 			3
#define ChPrinter		4
#define ChFont			5
#define PgSetup			6
#define Print			7
#define About			8
#define Portrait		9
#define Landscape		10
#define Header			11
#define Footer			12
#define Columns			13
#define Percent			14
#define ColumnText		15
#define HeaderText		16
#define FooterText		17
#define PercentText		18
#define Borders			19
#define UseBorder		20
#define Title			21


/* These next 2 blocks of data are used for a couple of user items I	*/
/* have. I use PPToPort to Blit them onto the screen. You you can click */
/* on them to select whether you want Portrait or Landscape printing.	*/

/* Data for Portrait (i.e., upright) icon */

Rect portraitRect  = {0x0000,0x0000,0x0010,0x0020};

char portraitIcon[] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xf0,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
0xf0,0xff,0xff,0xff,0xff,0xf0,0x0f,0xff,
0xf0,0xff,0xff,0xf0,0x0f,0xf0,0xf0,0xff,
0xf0,0xff,0xff,0x0f,0xf0,0xf0,0x00,0x0f,
0xf0,0xff,0xff,0x0f,0xf0,0xff,0xff,0x0f,
0xf0,0xff,0xff,0xf0,0x0f,0xff,0xff,0x0f,
0xf0,0xff,0xf0,0x0f,0xf0,0x0f,0xff,0x0f,
0xf0,0xff,0x0f,0xff,0xff,0xf0,0xff,0x0f,
0xf0,0xff,0x0f,0x0f,0xf0,0xf0,0xff,0x0f,
0xf0,0xff,0x0f,0x0f,0xf0,0xf0,0xff,0x0f,
0xf0,0xff,0x0f,0x0f,0xf0,0xf0,0xff,0x0f,
0xf0,0xff,0x0f,0x0f,0xf0,0xf0,0xff,0x0f,
0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,
0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

LocInfo	portraitLoc = {
			mode640,							/* portSCB		*/
			(Pointer) portraitIcon,				/* PixImagePtr	*/
			0x8,								/* RowBytes		*/
			0x0000,0x0000,0x0010,0x0020};		/* BoundsRect	*/

/* Data for Landscape (i.e., sideways) icon */

Rect landscapeRect = {0x0000,0x0000,0x0010,0x0020};

char landscapeIcon[] = {
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xf0,0x00,0x00,0x00,0x00,0x00,0x0f,0xff,
0xf0,0xff,0xff,0xff,0xff,0xf0,0xf0,0xff,
0xf0,0xff,0xff,0xff,0xff,0xf0,0x00,0x0f,
0xf0,0xf0,0x00,0x0f,0xff,0xff,0xff,0x0f,
0xf0,0xff,0xff,0xf0,0xff,0xff,0xff,0x0f,
0xf0,0xf0,0x00,0xf0,0xf0,0x0f,0xff,0x0f,
0xf0,0xff,0xff,0xff,0x0f,0xf0,0xff,0x0f,
0xf0,0xff,0xff,0xff,0x0f,0xf0,0xff,0x0f,
0xf0,0xf0,0x00,0xf0,0xf0,0x0f,0xff,0x0f,
0xf0,0xff,0xff,0xf0,0xff,0xff,0xff,0x0f,
0xf0,0xf0,0x00,0x0f,0xff,0xff,0xff,0x0f,
0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,
0xf0,0xff,0xff,0xff,0xff,0xff,0xff,0x0f,
0xf0,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};

LocInfo	landscapeLoc = {
			0x80,
			(Pointer) landscapeIcon,
			0x8,
			0x0000,0x0000,0x0010,0x0020};


/* Data for edittext items below										*/

char	columnDText[] = "\p1";
char	percentDText[]= "\p100";


/* These are the Dialog Item definitions. The \021's imbeded in some of	*/
/* the text items appear as Open-Apples on the Apple IIGS SHR screen.	*/

ItemTemplate quitButton =  {
			Quit,					/* Item ID number					*/
			140,450,154,590,		/* Bounding rectangle				*/
			buttonItem,				/* Item type						*/
			"\pQuit   \021Q",		/* Pointer to string for this item	*/
			0,						/* Initial value for the thing		*/
			0,						/* flags for this item				*/
			NULL};					/* Pointer to color table			*/

ItemTemplate chFileButton =  {
			ChFile,
			100,10,114,150,
			buttonItem,
			"\pChoose File   \021F",
			0,
			0,
			NULL};

ItemTemplate chPrButton =  {
			ChPrinter,
			120,10,134,150,
			buttonItem,
			"\pChoose Printer",
			0,
			0,
			NULL};

ItemTemplate chFontButton =  {
			ChFont,
			140,10,154,150,
			buttonItem,
			"\pChoose Font",
			0,
			0,
			NULL};

ItemTemplate pgSetupButton =  {
			PgSetup,
			100,450,114,590,
			buttonItem,
			"\pPage Setup",
			0,
			0,
			NULL};

ItemTemplate printButton =  {
			Print,
			120,450,134,590,
			buttonItem,
			"\pPrint   \021P",
			0,
			0,
			NULL};

ItemTemplate aboutButton =  {
			About,
			5,10,19,150,
			buttonItem,
			"\pAbout",
			0,
			0,
			NULL};

ItemTemplate portraitButton =  {
			Portrait,
			5,513,21,545,
			userItem,
			(Pointer) DrawUserItem,
			0,
			0,
			NULL};

ItemTemplate landscapeButton =  {
			Landscape,
			5,558,21,590,
			userItem,
			(Pointer) DrawUserItem,
			0,
			0,
			NULL};

ItemTemplate headerEdit =  {
			Header,
			35,10,50,590,
			editLine,
			"\p@d @t|@l|Page: @p",
			128,						/* Value = max text to enter */
			0,
			NULL};

ItemTemplate footerEdit =  {
			Footer,
			65,10,80,590,
			editLine,
			NULL,
			128,
			0,
			NULL};

ItemTemplate borderItem =  {
			Borders,
			100,175,154,425,
			userItem,
			(Pointer) DrawUserItem,
			0,
			0,
			NULL};

ItemTemplate borderCheck =  {
			UseBorder,
			85,440,0,0,
			checkItem,
			"\pDon't use borders",
			0,
			0,
			NULL};

ItemTemplate columnEdit =  {
			Columns,
			82,10,97,30,
			editLine,
			NULL,
			1,
			0,
			NULL};

ItemTemplate columnStat =  {
			ColumnText,
			85,40,94,100,
			statText+itemDisable,
			"\pColumns",
			0,
			0,
			NULL};

ItemTemplate headerStat =  {
			HeaderText,
			25,10,34,610,
			statText+itemDisable,
			"\pHeader:    {@D=Date, @T=Time, @L/@S=Lg/Sh File name, \
@P=Pg#, |=Field Divider}",
			0,
			0,
			NULL};

ItemTemplate footerStat =  {
			FooterText,
			55,10,64,110,
			statText+itemDisable,
			"\pFooter:",
			0,
			0,
			NULL};

ItemTemplate percentEdit =  {
			Percent,
			82,125,97,160,
			editLine,
			NULL,
			3,
			0,
			NULL};

ItemTemplate percentStat =  {
			PercentText,
			85,165,94,300,
			statText+itemDisable,
			"\p% of original size",
			0,
			0,
			NULL};

/* This bit of text is, admittedly, very confusing. What it represents is	*/
/* text that can be passed to the Dialog Manager for a LongStatText2 item.	*/
/* The Dialog Manager will then use LETextBox2 to print it out. All of the	*/
/* slash 1's are escape characters used by LETextBox2 to mark embedded		*/
/* format characters. For instance, "\1C\5\0" compiles into memory as		*/
/* "01 43 05 00" which tells LETextBox2 to set the foreground color to 5.	*/

char myTitle[] = "\1C\5\0\1J\1\0\1F\0\0\006\020Lister\1F\0\0\0\0";
ItemTemplate laserLister = {
			Title,
			0,200,20,400,
			longStatText2+itemDisable,
			myTitle,
			sizeof(myTitle) - 1,		/* Value = size of text to print	*/
			0,
			NULL};

/* Here is the template that describes the entire dialog box				*/
DialogTemplate PrintDialog = {
			30,20,190,620,				/* Size/Position of the window		*/
			false,						/* visible flag (it's invisible)	*/
			0,							/* refcon for my use				*/
			&quitButton,				/* list of items in dialog box		*/
			&chFileButton,
			&chPrButton,
			&chFontButton,
			&pgSetupButton,
			&printButton,
			&aboutButton,
			&landscapeButton,
			&portraitButton,
			&headerEdit,
			&percentEdit,
			&columnEdit,
			&footerEdit,
			&borderItem,
			&borderCheck,
			&columnStat,
			&headerStat,
			&footerStat,
			&percentStat,
			&laserLister,
			NULL};					/* terminate the list with a null pointer */


/* All of the following is data for the About box */

/* Pretty picture of an Apple used as an Icon Item */

char AppleIcon640[] = {0x00,0x00,0x00,0x00,0x22,0x00,0x40,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,
0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x8f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x88,0x8f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x88,0x88,0x8f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xf8,0x88,0x88,0xff,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0x88,0x88,0x88,0xff,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0x88,0x88,0x8f,0xff,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0x88,0x88,0xff,0xff,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xf8,0x88,0x8f,0xff,0x88,0xff,0x88,0x88,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xf8,0x88,0x88,0x88,0x8f,0xff,0x88,0x88,0x88,0x88,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x88,0x8f,0xf0,0xf0,
0x0f,0x0f,0xfe,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xef,0xff,0xf0,0xf0,
0x0f,0x0f,0xfe,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xfe,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xee,0xef,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xf6,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xf6,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xf6,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x66,0x6f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xf4,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xf4,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x4f,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x4f,0xf0,0xf0,
0x0f,0x0f,0xff,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x5f,0xf0,0xf0,
0x0f,0x0f,0xff,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x5f,0xf0,0xf0,
0x0f,0x0f,0xff,0xf5,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xf1,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x1f,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xf1,0x11,0x11,0x1f,0xff,0x11,0x11,0x11,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0x11,0x11,0xff,0xff,0xf1,0x11,0x1f,0xff,0xff,0xf0,0xf0,
0x0f,0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,0xf0,
0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf0,
0x0f,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf0,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

Pointer AppleIconPtr = AppleIcon640;

/* LongStatText2 string. Red man says "Cryptic markings are:			*/
/*			\1J\1\0			-> Turn on center justification				*/
/*			\S\014\0		-> Choose Shadow and underlining			*/
/*			Lister			-> Actually Printed							*/
/*			\S\0\0			-> Back to plain text						*/
/*			\r				-> embedded carriage return ok in TextBox2	*/
/*			v1.0			-> Printed centered and normal				*/
/* everything else is explained above.									*/

char AboutStr[] = "\1J\1\0\1S\014\0\
Lister\1S\0\0\r\
v1.0\r\
\r\
by: Keith Rollin\r\
September 1, 1988";

ItemTemplate okButton =  {
			ok,
			50,12,65,68,
			buttonItem,
			"\pOK",
			0,
			0,
			NULL};

ItemTemplate aboutText =  {
			3,
			10,90,60,250,
			longStatText2+itemDisable,
			AboutStr,
			sizeof(AboutStr)-1,
			0,
			NULL};

ItemTemplate appleIcon =  {
			4,
			5,8,0,0,
			iconItem+itemDisable,
			(Pointer) &AppleIconPtr,
			0,
			0,
			NULL};

DialogTemplate AboutDialog = {
			50,190,120,450,
			true,
			0,
			&okButton,
			&aboutText,
			&appleIcon,
			NULL};


/* Dialog and item templates for the "Now Printing..." Dialog box */

char NowPrintingStr[] =
		"\1J\1\0Now printing your document";
char CancelPrintingStr[] =
		"\1J\1\0To cancel printing, hold down the \021 key and press period (.)";

ItemTemplate NowPrinting =  {
			10,
			5,10,15,430,
			longStatText2+itemDisable,
			NowPrintingStr,
			sizeof(NowPrintingStr)-1,
			0,
			NULL};

ItemTemplate CmdDotToCancel =  {
			11,
			25,10,35,430,
			longStatText2+itemDisable,
			CancelPrintingStr,
			sizeof(CancelPrintingStr)-1,
			0,
			NULL};

DialogTemplate NowPrintingDialog = {
			90,100,130,540,
			true,
			0,
			&CmdDotToCancel,
			&NowPrinting,
			NULL};


/* Oooh! Look! Something new! This is a structure used in defining a	*/
/* special user item. I have a box that appears on the screen that		*/
/* represents the borders that are printed. Clicking on the lines of	*/
/* box turn certain borders on and off. The following array of the		*/
/* following structure defines the location and status of the borders.	*/

typedef struct BorderLine {
		boolean used;	/* defines if the border is to be used or not	*/
		Rect 	line;	/* A small rect that is used to draw and detect	*/
						/* clicks on the lines representing borders.	*/
		} BorderLine;

BorderLine myBorders[9] = {
		true, 100, 175, 154, 425,	/* surrounds entire box	*/
		true, 100, 175, 102, 425,	/* top line				*/
		true, 105, 175, 107, 425,	/* under header			*/
		false, 147, 175, 149, 425,	/* above footer			*/
		true, 152, 175, 154, 425,	/* bottom line			*/
		true, 100, 175, 154, 177,	/* left line			*/
		true, 100, 299, 154, 301,	/* center line			*/
		true, 100, 423, 154, 425};	/* right line			*/

/* This pattern is used to show disabled borders */

Pattern greyPat  = {0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33,
					0xCC,0xCC,
					0x33,0x33};

/* We define a couple of macros here for clarification purposes.	*/
/* In our source code later on, we do some rectangle metrics.		*/
/* While QuickDraw II does provide some glue routines for SetRect	*/
/* and InsetRect, I don't really want to have to go through the 	*/
/* Tool Dispatcher to such a trival thing. So I define them as		*/
/* macros here.														*/

#define SETRECT(rect, _top, _left, _bottom, _right)	\
	(rect).h1 = (_left), (rect).v1 = (_top), 	\
	(rect).h2 = (_right), (rect).v2 = (_bottom)

#define INSETRECT(rect, _dh, _dv)	\
	(rect).h1 += (_dh), (rect).v1 += (_dv), 	\
	(rect).h2 -= (_dh), (rect).v2 -= (_dv)


/* ************************************************************************* */
/*																			 */
/* This is the core of the program. It simply calls all the other routines   */
/* in order.																 */
/*																			 */
/* ************************************************************************* */
main()
{
	InitTools();			/* initialize tools								*/
	InitApp();				/* initialize application specific stuff		*/
	ShowCursor();
	EventLoop();			/* do your stuff								*/
	ShutDownApp();			/* until user wants to							*/
	ShutDownTools();		/*   do something else!							*/
}


/* ************************************************************************* */
/*																			 */
/* ErrorCheck - Called by InitTools to check for startup errors.			 */
/*																			 */
/* ************************************************************************* */

char DeathMsg[] =
	"Fatal error $xxxx has occurred at xxxx. Press any key to exit:";

void ErrorCheck(where)
int where;
{
      if (_toolErr)
     {
       Int2Hex(_toolErr,DeathMsg + 13 ,4);	/* Stick error # into a string	*/
       Int2Hex(where,DeathMsg + 34 ,4);		/* Put location # into a string */
	   GrafOff();							/* Turn off Super Hires			*/
	   WriteCString(DeathMsg);				/* Print the error message		*/
	   SysBeep();							/* Danger, Will Robinson!!!		*/
	   ReadChar(0);							/* Pause for a character		*/
	   ShutDownTools();						/* ShutDown my Tools			*/
	   exit(1);								/* Run like a coward			*/
     }
}


/* ************************************************************************* */
/*																			 */
/* InitTools - This routine start up all the tools that will be needed by    */
/* your application.														 */
/*																			 */
/* ************************************************************************* */

#define QDZPage		0
#define EMZPage		0x0300		/* QDZPage+0x0300 */
#define CtlZPage	0x0400		/* EMZPage+0x0100 */
#define MenuZPage	0x0500		/* CtlZPage+0x0100 */
#define LEZPage		0x0600		/* MenuZPage+0x0100 */
#define PMZPage		0x0700		/* LEZPage+0x0100 */
#define SFZPage		0x0900		/* PMZPage+0x0200 */
#define FMZPage		0x0A00		/* SFZPage+0x0100 */
#define ToolZPSize	0x0B00L		/* FMZPage+0x0100 */

InitTools()
{
	TLStartUp();								/* for calling tools */
	ErrorCheck(1);								/* Make sure all is OK      */
	MyID = MMStartUp();							/* ID for all transactions */
	ErrorCheck(2);								/* Make sure all is OK      */
	MTStartUp();								/* Misc. Tools  */
	ErrorCheck(3);								/* Make sure all is OK      */

	y = NewHandle(ToolZPSize,
		MyID,									/* put it to my name    */
		attrLocked+attrFixed+attrPage+attrBank,
		0L);
	ErrorCheck(4);								/* Make sure all is OK      */
	z = *y;										/* deref handle     */

	LoadTools(ToolTable);
	ErrorCheck(5);								/* Make sure all is OK      */

	QDStartUp((int) z + QDZPage,ThisMode,MAXSCAN,MyID);
	ErrorCheck(6);								/* Make sure all is OK      */

	QDAuxStartUp();
	ErrorCheck(16);

	EMStartUp((int) z + EMZPage,20,0,ScreenWidth,0,200,MyID);
	ErrorCheck(7);								/* Make sure all is OK      */

	MoveTo(30,30);
	SetBackColor(0);
	SetForeColor(0xf);
	DrawString("\pOne Moment Please ... ");

	WaitCursor();
	ShowCursor();

	WindStartUp(MyID);
	ErrorCheck(8);								/* Make sure all is OK      */

	CtlStartUp(MyID,(int) z + CtlZPage);
	ErrorCheck(9);								/* Make sure all is OK      */

	MenuStartUp(MyID,(int) z + MenuZPage);
	ErrorCheck(10);								/* Make sure all is OK      */

	LEStartUp(MyID,(int) z + LEZPage);
	ErrorCheck(11);								/* Make sure all is OK      */

	DialogStartUp(MyID);
	ErrorCheck(12);								/* Make sure all is OK      */

	ScrapStartUp();						/* no errors possible from these 2	*/
	DeskStartUp();							/* All we need is init'ed now	*/

	/* Insert application specific tools here */

	PMStartUp(MyID, (int) z + PMZPage );
	ErrorCheck(15);

	SFStartUp(MyID, (int) z + SFZPage);
	ErrorCheck(13);

	FMStartUp(MyID, (int) z + FMZPage);			/* the watch cursor is up	*/
	ErrorCheck(14);                             /* while we count the fonts */

	PMLoadDriver(bothDrivers);	/* read this in while "Plz w8" is on screen	*/

	RefreshDesktop(0L);


}

/* ************************************************************************* */
/*																			 */
/* ShutDownTools - Shuts down all of the tools started by InitTools().		 */
/*																			 */
/* ************************************************************************* */

ShutDownTools()
{
	DeskShutDown();
	FMShutDown();
	PMShutDown();
	SFShutDown();
	ScrapShutDown();
	DialogShutDown();
	LEShutDown();
	MenuShutDown();
	CtlShutDown();
	WindShutDown();
	EMShutDown();
	QDAuxShutDown();
	QDShutDown();
	MTShutDown();

	DisposeHandle(y);

	MMShutDown(MyID);
	TLShutDown();
}


/* ************************************************************************* */
/*																			 */
/* InitApp - Create the Dialog box. Get and validate a Print handle. Set up	 */
/* some default values (50% reduction, 2 columns, 12 pt Courier, condensed	 */
/* print, sideways printing). Dim the "Print" button until a file is selected*/
/* for printing.															 */
/*																			 */
/* ************************************************************************* */
InitApp()
{
	CtlRecHndl	ctrlItem;
	void ShowError();

	myDialog = GetNewModalDialog(&PrintDialog);
	SetPort((GrafPortPtr)myDialog);

	/* Get a print handle, set up system defaults, then add my own.			*/
	PrintHndl = (PrRecHndl) NewHandle(140L,
			 MyID,
			 attrNoCross+attrLocked,
			 0L);
	ErrorCheck(17);

	PrDefault(PrintHndl);
	ShowError(PrError());

	/* Put us into condensed mode											*/

	SetVSizing(2);

	/* Set number of columns to 2, initialize column EditLine string,		*/
	/* update Percent Reduction												*/
	numCols = 2;
	Int2Dec(numCols,&columnDText[1],1,false);
	SetIText(myDialog,Columns,columnDText);
	SetReduction(100/numCols);

	/* Set orientation to sideways and update Print Handle					*/
	sideways = true;
	SetOrientation(sideways);

	/* Default printout font												*/
	theFont = (12 << 24) + (0 << 16) + 22; /*12 pt. plain Courier			*/

	/* Dim the "Print" item until we get a valid file						*/
	ctrlItem = GetControlDItem(myDialog,Print);
	HiliteControl(255,ctrlItem);

	ShowWindow(myDialog);

	InitCursor();
}



/* ************************************************************************* */
/*																			 */
/* ShutDownApp - This routine is called once at the end of your program, but */
/* before the tools are shut down. Take this opportunity to clean up after   */
/* yourself if you need to.													 */
/*																			 */
/* ************************************************************************* */
ShutDownApp()
{
	/* Currently does nothing */
}



/* ************************************************************************* */
/*																			 */
/* ShowError
/*																			 */
/* ************************************************************************* */

void ShowError(ErrNum)
int	ErrNum;
{
	ptr ErrStringPtr;
	
	if (ErrNum != 0) {
		switch(ErrNum) {
			case prAbort:
				ErrStringPtr = ErrCanceled;
				break;
			case missingDriver:
				ErrStringPtr = ErrMissingDriver;
				break;
			case portNotOn:
				ErrStringPtr = ErrPortNotOn;
				break;
			case noPrintRecord:
				ErrStringPtr = ErrNoPrintRecord;
				break;
			case badLaserPrep:
				ErrStringPtr = ErrBadLaserPrep;
				break;
			case badLPFile:
				ErrStringPtr = ErrBadLPFile;
				break;
			case papConnNotOpen:
				ErrStringPtr = ErrPapConnNotOpen;
				break;
			case papReadWriteErr:
				ErrStringPtr = ErrPapReadWriteErr;
				break;
			case 0x1308:	/* new error code (Sys Disk 3.2) - PntrConFailed */
				ErrStringPtr = ErrPntrConFailed;
				break;
			default:
				ErrStringPtr = 0;
		} /* switch */
		
		if (ErrStringPtr != 0) {
			AlertWindow(0,NULL,ErrStringPtr);
		} else {
			Int2Hex(PrError(),PrintErrNum,4);
			AlertWindow(0,SubStrings,ErrGeneric);
		}
		
	} /* If (ErrNum) */
} /* ShowError() */
	
	
/* ************************************************************************* */
/*																			 */
/*	Bring up the About box in response to clicking on the About button.		 */
/*	This routine employs some high-powered pyro-techniks designed by 		 */
/*	Industrial Light and Magic when displaying the About box, to wit, it	 */
/*	draws a series of rectangles to creating that lovely Zooming effect.	 */
/*	When closing the box, the process is reversed. Neat, huh!?!				 */
/*																			 */
/* ************************************************************************* */

#define ZOOMMAX 9
void doAbout()
{
	GrafPortPtr tDialog;
	Rect		startRect,newRect,oldRect,endRect,deltaRect;
	int			i;

	/* Zoom out from the About button to where the About window will be */

	startRect = aboutButton.itemRect;
	endRect = AboutDialog.dtBoundsRect;
	INSETRECT(endRect,-12,-5);
	GlobalToLocal((PointPtr) &endRect.v1);
	GlobalToLocal((PointPtr) &endRect.v2);

	/* define some delta constants for speed							*/
	SETRECT(deltaRect,
				endRect.v1-startRect.v1,
				endRect.h1-startRect.h1,
				endRect.v2-startRect.v2,
				endRect.h2-startRect.h2);

	/* set up my pen size and mode										*/
	SetPenSize(2,1);
	SetPenMode(notXOR);

	/* Now draw appropriately sized rectangles							*/
	for (i = 1; i <= ZOOMMAX; i++) {
		SETRECT(newRect,
					startRect.v1 + (deltaRect.v1*i)/ZOOMMAX,
					startRect.h1 + (deltaRect.h1*i)/ZOOMMAX,
					startRect.v2 + (deltaRect.v2*i)/ZOOMMAX,
					startRect.h2 + (deltaRect.h2*i)/ZOOMMAX);
		FrameRect(&newRect);
	}

	/* Then erase all of them but the last one. It will stay around		*/
	/* and pretend like it was supposed to be there all along by    	*/
	/* looking just like an Alert window's frame.						*/

	for (i = 1; i < ZOOMMAX; i++) {
		SETRECT(newRect,
					startRect.v1 + (deltaRect.v1*i)/ZOOMMAX,
					startRect.h1 + (deltaRect.h1*i)/ZOOMMAX,
					startRect.v2 + (deltaRect.v2*i)/ZOOMMAX,
					startRect.h2 + (deltaRect.h2*i)/ZOOMMAX);
		FrameRect(&newRect);
	}

	/* Now redraw the last rectangle in modeOr. We originally drew it	*/
	/* in modeXOR, which created weird color effects. So redraw it with */
	/* the right colors. 												*/

	SetPenMode(modeCopy);
	FrameRect(&endRect);

	/* Now empty out the inside of the rectangle. This would have been	*/
	/* done by the Window Manager anyway when it drew the Alert window,	*/
	/* but I want to do it now for speed.								*/

	INSETRECT(endRect,2,1);
	EraseRect(&endRect);

	/* Finally, all of the fun stuff is over. Get and handle the Modal	*/
	/* Dialog, and then close it.										*/

	tDialog = GetNewModalDialog(&AboutDialog);
	ModalDialog(NULL);
	CloseDialog(tDialog);

	/* Now reverse the entire process of zooming the window.			*/

	SetPort((GrafPortPtr)myDialog);
	SetPenSize(2,1);
	SetPenMode(notXOR);

	for (i = ZOOMMAX; i >= 1; i--) {
		SETRECT(newRect,
					startRect.v1 + (deltaRect.v1*i)/ZOOMMAX,
					startRect.h1 + (deltaRect.h1*i)/ZOOMMAX,
					startRect.v2 + (deltaRect.v2*i)/ZOOMMAX,
					startRect.h2 + (deltaRect.h2*i)/ZOOMMAX);
		FrameRect(&newRect);
	}

	for (i = ZOOMMAX; i >= 1; i--) {
		SETRECT(newRect,
					startRect.v1 + (deltaRect.v1*i)/ZOOMMAX,
					startRect.h1 + (deltaRect.h1*i)/ZOOMMAX,
					startRect.v2 + (deltaRect.v2*i)/ZOOMMAX,
					startRect.h2 + (deltaRect.h2*i)/ZOOMMAX);
		FrameRect(&newRect);
	}

	PenNormal();
}



/* ************************************************************************* */
/*																			 */
/*	This routine sets the condensed/middle/normal vertical aspect ratios	 */
/*	in the print record for the currently selected printer. Enter with:		 */
/*
/*		0 = Normal
/*		1 = Intermediate
/*		2 = Condensed
/*																			 */
/* ************************************************************************* */

void SetVSizing(newSize)
int	newSize;
{
	int printer;

	theVSizing = newSize;
	printer = (**PrintHndl).prInfo.iDev;	/* what kind of printer?		*/
	if (printer == 1) {

		/* We have an ImageWriter. This printer does not have an Inter-		*/
		/* mediate mode, so we force that into Condensed mode.				*/

		switch (newSize) {
			case 0:
				(**PrintHndl).prStl.wDev |=  0x0004;	/* Set bit 2		*/
				break;
			case 1:
			case 2:
				(**PrintHndl).prStl.wDev &= ~0x0004;	/* Clear bit 2		*/
				break;
		}
	} else {

		/* We are dealing with a LaserWriter. It can take whatever we throw	*/
		/* at it.															*/

		(**PrintHndl).prStl.vSizing = newSize;
	}
}


/* ************************************************************************* */
/*																			 */
/*	This routine takes a suggested amount of percent reduction, and sets the */
/*	print record accordingly. In the case of the ImageWriter, not all red-	 */
/*	unctions are possible, so the suggested amount of reduction must be 	 */
/*	adjusted accordingly. When all of that has been done, convert the amount */
/*	of reduction to text, and set that as the Percent Reduction EditLine	 */
/*	field.																	 */
/*																			 */
/* ************************************************************************* */

void SetReduction(suggestedReduction)
int	suggestedReduction;
{
	int printer;

	printer = (**PrintHndl).prInfo.iDev;	/* what kind of printer?		*/
	if (printer == 1) {

		/* We are dealing with an ImageWriter. These little beaties can 	*/
		/* only print in 100% or 50% reduction. Therefore, examine the		*/
		/* suggested amount of reduction, and if it is over 75%, set it to	*/
		/* 100%; if it is under 75%, set it to 50%. Then set or clear the 	*/
		/* reduction bit in the wDev field appropriately (0 for 50%, 1 for	*/
		/* full sized).														*/

		if (suggestedReduction > 75) {
			theReduction = 100;
			(**PrintHndl).prStl.wDev |=  0x0008;	/* Set bit 3			*/
		} else {
			theReduction = 50;
			(**PrintHndl).prStl.wDev &= ~0x0008;	/* Clear bit 3			*/
		}
	} else {

		/* We are dealing with a LaserWriter. If it is between 25 and 400	*/
		/* inclusive, then set theReduction to it, and set the appropriate	*/
		/* field in the print record. If it is NOT between 25 and 400, then	*/
		/* ignore 'suggestedReduction' and reset the EditLine item back to	*/
		/* the previous value of theReduction.								*/

		if ((suggestedReduction >= 25) && (suggestedReduction <= 400)) {
			theReduction = suggestedReduction;
			(**PrintHndl).prStl.reduction = theReduction;
		}
	}
	Int2Dec(theReduction,&percentDText[1],(theReduction>99)?3:2,false);
	percentDText[0] = (char)(theReduction>99)?3:2;
	SetIText(myDialog,Percent,percentDText);
}


/* ************************************************************************* */
/*																			 */
/*	This routine is called from all over the place to set the orientation	 */
/*	bit (bit 1 of prStl.wDev) in the print record according to the following */
/*	table:																	 */
/*																			 */
/*						Portrait	Landscape
/*						(upright)	(sideways)
/*		LaserWriter			0			1
/*		ImageWriter			1			0
/*																			 */
/*																			 */
/* ************************************************************************* */

void SetOrientation(sideways)
boolean sideways;
{
	int printer;

	printer = (**PrintHndl).prInfo.iDev;	/* get printer type				*/

	/* If the printer is an ImageWriter and we are printing sideways,		*/
	/* or the printer is a LaserWriter and we are printing upright,			*/
	/* clear the orientation bit (bit 1 of wDev). Otherwise, set it.		*/

	if (((printer==1) && sideways) || ((printer==3) && (!sideways))) {
		(**PrintHndl).prStl.wDev &= ~0x0002;	/* Clear bit 1				*/
	} else {
		(**PrintHndl).prStl.wDev |=  0x0002; 	/* Set bit 1				*/
	}
}


/* ************************************************************************* */
/*																			 */
/*	The user has clicked on one of the orientation icons. Find out which one */
/*	determine if we need to change their states, and invert the icons if we	 */
/*	do. The call a routine to set the appropriate field in the print record. */
/*																			 */
/* ************************************************************************* */

void doPrDirection(what)
word	what;
{
	if (((what == Portrait) && (sideways)) ||
		((what == Landscape) && (!sideways))) {

		/* What the above line says is: "Has the setting changed?" If so,	*/
		/* we invert the icons, change our 'sideways' variable, and call a	*/
		/* routine to set the Print record.									*/

		InvertRect(&portraitButton.itemRect);
		InvertRect(&landscapeButton.itemRect);
		sideways = !sideways;
		SetOrientation(sideways);
	}
}


/* ************************************************************************* */
/*																			 */
/*	An event occurred in the "Percent Reduction" field. Get the text that's	 */
/*	there, turn it into a number, and validate it.							 */
/*																			 */
/* ************************************************************************* */

void doPercent()
{
	char	theText[5];
	int 	temp;

	GetIText(myDialog,Percent,theText);	/* Get the text as a Pascal String	*/

	/* set temp = value of text in columns EditLine item					*/
	if ((int)(*theText) > 0) {
		temp = Dec2Int(&theText[1],(int)(*theText),false);
	} else {
		temp = 0;
	}
	if (temp != theReduction) SetReduction(temp);
}


/* ************************************************************************* */
/*																			 */
/*	An event occured in the "Number of Columns" EditLine box. Get the		 */
/*	text that's in there, turn it into a number, and validate that number.	 */
/*																			 */
/* ************************************************************************* */

void doColumns()
{
	char	theText[5];
	int 	temp;

	GetIText(myDialog,Columns,theText);	/* Get the text as a Pascal String	*/
	if ((int) (*theText) > 0) {			/* If length > 0, validate it.		*/

		/* set temp = value of text in columns EditLine item				*/
		temp = Dec2Int(&theText[1],(int)(*theText),false);

		/* If it is between 1 and 4 inclusive, then set numCols to it, and	*/
		/* fabricate an appropriate value for the amount of reduction. If	*/
		/* it is NOT between 1 and 4, then reset the EditLine item back to	*/
		/* the previous value of numCols.									*/

		if ((temp > 0) && (temp < 5)) {
			numCols = temp;
			SetReduction(100/numCols);
		} else {
			Int2Dec(numCols,&columnDText[1],1,false);
			SetIText(myDialog,Columns,columnDText);
		}
	}
}


/* ************************************************************************* */
/*																			 */
/*	The Use Borders check box has been clicked on. Invert its value, and	 */
/*	redraw the border lines.												 */
/*																			 */
/* ************************************************************************* */

void doCheckBox()
{
	myBorders[0].used = !myBorders[0].used;
	SetDItemValue(myBorders[0].used?0:1,myDialog,UseBorder);
	DrawUserItem(myDialog,Borders);
}


/* ************************************************************************* */
/*																			 */
/*	This routine is called if we click on our border control. Find out		 */
/*	which one was clicked on, invert its status, and redraw it.				 */
/*																			 */
/* ************************************************************************* */

void doBorders()
{
	Point	*mouseLoc;
	int		index;
	void DrawLine();

	GetMouse(mouseLoc);					/* Find out where we clicked		*/

	for (index=1;						/* See if we hit a border line		*/
		!(PtInRect(mouseLoc,&myBorders[index].line) || (index == 8));
		index++ );

	if (PtInRect(mouseLoc,&myBorders[index].line)) {	/* invert it if so	*/
		myBorders[index].used = !myBorders[index].used;
		DrawLine(index);
		PenNormal();
	}
}


/* ************************************************************************* */
/*																			 */
/*	Called in response to clicking on the File button. It calls SFGetFile	 */
/*	to get a file name. If we get one, then we activate the Print button.	 */
/*	if we don't get one, then we deactivate that button.					 */
/*																			 */
/* ************************************************************************* */

void doChFile()
{
	CtlRecHndl ctrlItem;

	SFGetFile(120,20,prompt,NULL,myTypes,&myReply);
	if (myReply.good) {
		ctrlItem = GetControlDItem(myDialog,Print);	/* Activate Print button */
		HiliteControl(0,ctrlItem);
	} else {
		ctrlItem = GetControlDItem(myDialog,Print);	/* Deactivate it.		*/
		HiliteControl(255,ctrlItem);
	}
}


/* ************************************************************************* */
/*																			 */
/*	Called in response to clicking on the Choose Printer button. It calls 	 */
/*	PrChoosePrinter() to select the printer, sets default values in the		 */
/*	print record, and then restores as many of them as we can to the settings*/
/*	wehad before the change was made.										 */
/*																			 */
/* ************************************************************************* */

void doChPrinter()
{
	boolean dummy;
	word	oldFeed, oldPaperType;

	oldFeed = (**PrintHndl).prStl.feed;
	oldPaperType = (**PrintHndl).prStl.paperType;

	dummy = PrChooser();
	PrDefault(PrintHndl);
	SetOrientation(sideways);
	SetReduction(theReduction);
	SetVSizing(theVSizing);

	(**PrintHndl).prStl.feed = oldFeed;
	(**PrintHndl).prStl.paperType = oldPaperType;
}


/* ************************************************************************* */
/*																			 */
/*	This routine is called in response to pressing the Choose Font button.	 */
/*	It simply calls ChooseFont() and saves the result in 'theFont'. However, */
/*	page 8-27 of the ToolBox reference manual says that ChooseFont auto-	 */
/*	matically installs the font chosen. For this reason, I save off the 	 */
/*	current font before calling ChooseFont(), and restore it afterwards.	 */
/*																			 */
/* ************************************************************************* */

void doChFont()
{
	LongWord oldFont;

	/* There is a bug in some System Disks that cause the computer to hang	*/
	/* if we call ChooseFont while there are updates pending in the dialog	*/
	/* box. Let's get rid of them before we call it, why don't we...		*/

	BeginUpdate(myDialog);
	DrawDialog(myDialog);
	EndUpdate(myDialog);

	oldFont = FMGetCurFID();			/* Save the old port font			*/
	theFont =  ChooseFont(theFont,0);	/* Get the new one					*/
	InstallFont(oldFont,0);				/* Set the old port font back		*/
}


/* ************************************************************************* */
/*																			 */
/*	This routine is called in response to pressing the Page Setup button.	 */
/*	It calls the Page Setup routine in the Print Manager. It then sets up	 */
/*	some variables that we maintain based on the new settings in the Print	 */
/*	record. For instance, it will invert the correct Portrait or Landscape	 */
/*	icon, and will set the right number in the % Reduction box.				 */
/*																			 */
/* ************************************************************************* */

void doPgSetup()
{
	int dummy, printer,bit1;
	boolean newSideways;


	if (PrintHndl != NULL) {
		dummy = PrStlDialog(PrintHndl);		/* Call PrStlDialog				*/
		printer = (**PrintHndl).prInfo.iDev;/* determine type of printer	*/


		/* Update our % reduction display and VSizing variable.				*/

		if (printer == 1) {
			SetReduction(((**PrintHndl).prStl.wDev & 0x0008)?100:50);
			if (((**PrintHndl).prStl.vSizing & 0x0004) == 0) {
				theVSizing = 2;
			} else {
				theVSizing = 0;
			}
		} else {
			SetReduction((**PrintHndl).prStl.reduction);
			theVSizing = (**PrintHndl).prStl.vSizing;
		}



		/* Determine whether the user selected Portrait or Landscape		*/
		/* printing. If the orientation changed, then invert our icons.		*/

		bit1 = (**PrintHndl).prStl.wDev & 0x0002;  /* get bit 1				*/
		switch (printer) {
			case 1: newSideways = (bit1==0); break;
			case 3: newSideways = (bit1!=0); break;
			default: newSideways = false; break;
		}
		if (sideways != newSideways) {
			sideways = newSideways;
			InvertRect(&portraitButton.itemRect);
			InvertRect(&landscapeButton.itemRect);
		}
	}
}


/* ************************************************************************* */
/*																			 */
/*	Open the file whose name is in SFReplyRec. If the call is good, save	 */
/*	the refnum, and put the file into NewLine mode. In this mode, ProDOS	 */
/*	will read characters into a buffer until a certain character is read.	 */
/*	In this case, we will read characters until a carriage return is read.	 */
/*																			 */
/* ************************************************************************* */

int OpenFile()
{
	if (myReply.good) {			/* Check to see if we have a valid record	*/
		OPEN(&OpenParms);		/* Open the file							*/
		if (OpenParms.openRefNum != 0) {	/* Put it into NewLine mode		*/
			NewlineParms.newLRefNum = OpenParms.openRefNum;
			IOParms.fileRefNum = OpenParms.openRefNum;
			NEWLINE(&NewlineParms);
		}
		return(OpenParms.openRefNum);	/* return refnum for opened file	*/
	} else {
		return(0);				/* If record is bad, return 0 for refnum	*/
	}
}


/* ************************************************************************* */
/*																			 */
/*	Read a line from the file that we opened. Tack on a NULL at the end of	 */
/*	line to appease C, and return the number characters read.				 */
/*																			 */
/* ************************************************************************* */

int ReadFile()
{
	READ(&IOParms);
	line[IOParms.transferCount] = '\0';
	return(IOParms.transferCount);
}


/* ************************************************************************* */
/*																			 */
/*	Close the file that we are reading.										 */
/*																			 */
/* ************************************************************************* */

void CloseFile()
{
	CLOSE(&IOParms);
}


/* ************************************************************************* */
/*																			 */
/*	Buffers for the routines that print the Header and Footer.				 */
/*																			 */
/* ************************************************************************* */

char HeaderFooter[255];
char BuiltHF[255];


/* ************************************************************************* */
/*																			 */
/*	Handy formula that takes a column number and returns the coordinate of	 */
/*	left margin for that column.											 */
/*																			 */
/* ************************************************************************* */

int SetMargin(col)
int	col;
{
	return(((*myPagePtr).h2-(*myPagePtr).h1)*col / numCols + (*myPagePtr).h1);
}


/* ************************************************************************* */
/*																			 */
/*	This routine take the string that is stored in the HeaderFooter string	 */
/*	buffers, and starts parsing it, starting at the character indexed by the */
/*	variable 'src'. It will read up until the ned of the string or the next	 */
/*	'|', parsing embedded commands, and placing the resulting string into	 */
/*	'BuiltHF'.																 */
/*																			 */
/*	Special substitutions ('@' marks embedded command):
/*		D:	replace with current date.
/*		T:	replace with current time.
/*		L:	replace with full path name.
/*		S:	replace with file name (short pathname).
/*		P:	replace with page number.
/*																			 */
/* ************************************************************************* */

int GetLineSegment(src)
int	src;
{
	int   dest,i,max,sLen;
	char	pageText[3];

	dest = 0;
	sLen = (int) HeaderFooter[0];

	while ((HeaderFooter[src] != '|') && (src <= sLen)) {
		if (HeaderFooter[src] == '@') {
			src++; 								/* Skip over the '@'		*/
			switch (HeaderFooter[src++]) { 		/* skips over format code	*/
				case 'd':
				case 'D':
					for (i=0;i<=7; BuiltHF[dest++] = (time[i++] & 0x7f));
					break;
				case 't':
				case 'T':
					max = ReadBParam(0x35) ? 16 : 19;
					for (i=9; i<=max; BuiltHF[dest++] = (time[i++] & 0x7f));
					break;
				case 'l':
				case 'L':
					max = (int) myReply.fullPathname[0];
					for (i=1; i<=max; BuiltHF[dest++] = myReply.fullPathname[i++]);
					break;
				case 's':
				case 'S':
					max = (int) myReply.filename[0];
					for (i=1; i<=max; BuiltHF[dest++] = myReply.filename[i++]);
					break;
				case 'p':
				case 'P':
					Int2Dec(pageNum,pageText,3,false);
					for (i=0; i<=2; BuiltHF[dest++] = pageText[i++]);
					break;
				default:
					src -= 2;		/* Go back to the '@' and copy it		*/
					BuiltHF[dest++] = HeaderFooter[src++];
					/* end up pointing to ex-format code, and printing it	*/
					break;
			}  /* end switch */
		} else {
			BuiltHF[dest++] = HeaderFooter[src++];
		}  /* == '@' */
	}		/* == '|' */
	BuiltHF[dest] = '\0'; /* replace the '|' with a NULL, ending the string */
	return(++src);		/* skip over the '|' */
}


/* ************************************************************************* */
/*																			 */
/*	If we are printing in PostScript mode, we need to fix up the string. The */
/*	characters "/", "(", and ")" are special to PostScript. In order to		 */
/*	print them, we need to stick a "\" in front of them.					 */
/*																			 */
/* ************************************************************************* */

void FixString(string)
char string[];
{
	char strCopy[255];
	char *srcPtr,*destPtr;
	char ch;

	srcPtr = string;			/* Set up pointers to the source string and	*/
	destPtr = strCopy;			/* to the buffer where a copy of it will go	*/

	while ( *destPtr++ = *srcPtr++);	/* Make a copy of the string		*/

	srcPtr = strCopy;					/* reset our pointers				*/
	destPtr = string;

	while ((ch = *srcPtr++) != 0) {		/* Now fix all of the characters	*/
		if ((ch == '\\') || (ch == '(') || (ch == ')')) {
			*destPtr++ = '\\';
		}
		if (ch != 0x0d) *destPtr++ = ch;	/* and remove carriage returns!	*/
	}
	*destPtr = '\0';					/* make this thing a C String		*/
}

/* ************************************************************************* */
/*																			 */
/*	Sends the specified string out at the specified location. This routine	 */
/*	determines if we are in PostScript mode, and will create a PostScript	 */
/*	command string if so. If not, it will simply use QuickDraw to draw the	 */
/*	string.																	 */
/*																			 */
/* ************************************************************************* */

void SendString(line,x,y)
char	line[];
int		x,y;
{
	char	xStr[6],yStr[6],printStr[255];

	if (PostScript) {
		FixString(line);			/* Escape out all '\', '(', and ')'		*/
		Int2Dec(x,xStr,5,true);		/* Put XY coordinates into ASCII		*/
		Int2Dec(y,yStr,5,true);
		xStr[5] = '\0';				/* Int2Dec just returns the ASCII chars	*/
		yStr[5] = '\0';				/* We need to turn them into C Strings	*/
		strcpy(printStr,xStr);		/* Build a moveTo PostScript command	*/
		strcat(printStr,yStr);
		strcat(printStr,PSMoveStr);
		strcat(printStr,PSShow1);
		strcat(printStr,line);		/* Catenate the string to print.		*/
		strcat(printStr,PSShow2);	/* Catenate a 'show' command.			*/
		DrawCString(printStr);		/* Send it to LaserWriter driver.		*/
	} else {
		MoveTo(x,y);				/* Use QuickDraw to position and		*/
		DrawCString(line);			/* display the string.					*/
	}
}


/* ************************************************************************* */
/*																			 */
/*	Takes a string assumed to be a header or footer, and prints it where	 */
/*	it is told to. This routine gets the current time, and then calls		 */
/*	GetLineSegment() to get part of the Header/Footer and parse out the		 */
/*	special codes in it. Then it calls SendString to print it out. It does	 */
/*	this 3 times for the 3 possible segments of the Header/Footer.			 */
/*																			 */
/* ************************************************************************* */

void DrawHeaderFooter(v,left,right)
int v,left,right;
{
	int		src,width;

	ReadAsciiTime(time);			/* Get the current time for parsing		*/
	src = 1;						/* Start parsing the string at the start */
	src = GetLineSegment(src);		/* Get a parsed line segment. Set src.	*/
	SendString(BuiltHF,left+10,v);	/* Print it in the left slot			*/

	src = GetLineSegment(src);		/* Get another segment where we left off */
	width = CStringWidth(BuiltHF);	/* Find width in order to center it		*/
	SendString(BuiltHF,(right+left-width)/2,v);	/* Print it centered		*/

	GetLineSegment(src);			/* Get the last segment.				*/
	width = CStringWidth(BuiltHF);	/* Get width to right justify it		*/
	SendString(BuiltHF,right-10-width,v);		/* and print it.			*/
}


/* ************************************************************************* */
/*																			 */
/*	Read the Header string from the EditLine item, and call upon			 */
/*	DrawHeaderFooter() to format it, position it, and print it.				 */
/*																			 */
/* ************************************************************************* */

void PrintHeader(column)
int column;
{
	int row;

	GetIText(myDialog,Header,HeaderFooter);
	row = (*myPagePtr).v1 + lineHeight + lineHeight/2;
	DrawHeaderFooter(row,SetMargin(column),SetMargin(column+1));
}


/* ************************************************************************* */
/*																			 */
/*	Read the Footer string from the EditLine item, and call upon			 */
/*	DrawHeaderFooter() to format it, position it, and print it.				 */
/*																			 */
/* ************************************************************************* */

void PrintFooter(column)
int column;
{
	int row;

	GetIText(myDialog,Footer,HeaderFooter);
	row = (*myPagePtr).v2 - lineHeight + lineHeight/2;
	DrawHeaderFooter(row,SetMargin(column),SetMargin(column+1));
}


/* ************************************************************************* */
/*																			 */
/*	Draw any borders needed. If the Global "Borders Needed" flag			 */
/*	(myBorders[0].used) is FALSE, then do nothing. Else, check the flags for */
/*	each part of the borders array, and print the border line if appropriate */
/*																			 */
/* ************************************************************************* */

void DrawPageBorders()
{
	int		v1,v2,h1,h2;
	int		margin,column;

	v1 = (*myPagePtr).v1;
	h1 = (*myPagePtr).h1;
	v2 = (*myPagePtr).v2;
	h2 = (*myPagePtr).h2;

	if (myBorders[0].used) {
		if (myBorders[1].used) {
			MoveTo(h1,v1);
			LineTo(h2,v1);
		}
		if (myBorders[2].used) {
			MoveTo(h1,v1+lineHeight*2);
			LineTo(h2,v1+lineHeight*2);
		}
		if (myBorders[3].used) {
			MoveTo(h1,v2-lineHeight*2);
			LineTo(h2,v2-lineHeight*2);
		}
		if (myBorders[4].used) {
			MoveTo(h1,v2);
			LineTo(h2,v2);
		}
		if (myBorders[5].used) {
			MoveTo(h1,v1);
			LineTo(h1,v2);
		}
		if (myBorders[7].used) {
			MoveTo(h2,v1);
			LineTo(h2,v2);
		}
		if ((myBorders[6].used) && (numCols > 1)) {
			for (column = 1; column < numCols; column++) {
				margin = SetMargin(column);
				MoveTo(margin,v1);
				LineTo(margin,v2);
			} /* end for */
		} /* end if myBorders[6] etc. */
	} /* end of "if any borders are used at all" */
}


/* ************************************************************************* */
/*																			 */
/*	Print a single column. It prints any headers and/or footers, and then	 */
/*	loops through the number of lines printable on the page. For every time	 */
/*	through the loop, it reads a line from the file, and calls SendString	 */
/*	to print it in thr right location and right mode (PostScript or Quick-	 */
/*	Draw II). If we reached the end of the file, return this fact to 		 */
/*	PrintAPage().															 */
/*																			 */
/* ************************************************************************* */

int PrintAColumn(column)
int	column;
{
	int lineNum,margin;
	boolean done;

	if (haveHeader) PrintHeader(column);
	if (haveFooter) PrintFooter(column);
	lineNum = 1;
	margin = SetMargin(column);
	do {
		done = (ReadFile() == 0);
		if (!done) SendString(line,margin+10,lineNum*lineHeight+lineTop);
	} while ((!done) && (lineNum++ < maxLines));
	return (done);
}


/* ************************************************************************* */
/*																			 */
/*	Called by the heart of the Print Loop to print a single page. This		 */
/*	routine draws the borders, sets up the printer for PostScript input		 */
/*	if it is a LaserWriter, prints 'numCols' number of columns, and then	 */
/*	turns off PostScript mode if appropriate. This routine also detects if	 */
/*	we reached the end of the file or not, and sends that bit of information */
/*	back to the Print Loop to tell it to stop when it's time.				 */
/*																			 */
/* ************************************************************************* */

boolean PrintAPage()
{
	int		column, prErrorSave;
	boolean done;

	column = 0;
	MoveTo(20,20);							/* force a font to be set		*/
	DrawChar(' ');
	DrawPageBorders();

	if (PostScript) {
		PicComment(PostScriptBegin,0,NIL);	/* Turn on PostScripting		*/
		PicComment(TextIsPostScript,0,NIL);	/* Set form of PostScripting	*/
	}

	do {									/* Print x number of columns	*/
		done = PrintAColumn(column);
		column++;
		pageNum++;
	} while ((!done) && (column < numCols));

 	if (PostScript) {

		/* If an error occurred, PrError() would cause all Print Manager	*/
		/* routines to short-circuit themselves. If so, then we won't be	*/
		/* able to turn off PostScripting! So we have to save the current	*/
		/* value of PrError(), set it to zero, turn off PostScripting, and	*/
		/* then set PrError() back to its old value. NOTE: This is only a	*/
		/* problem with System Disk 3.2; the Print Manager on System Disk	*/
		/* 4.0 clears PostScript mode on PrClosePage().						*/

		prErrorSave = PrError();
		PrSetError(0);
		PicComment(PostScriptEnd,0,NIL);	/* Turn off PostScripting		*/
		PrSetError(prErrorSave);
	}

	return(done);	/* Tell Print Loop if this was the Last Page or not		*/
}


/* ************************************************************************* */
/*																			 */
/*	Routine called in response to clicking on the Print button. It sets up	 */
/*	some variables, and then goes into the Classic Print Loop:				 */
/*																			 */
/*		Display Job Dialog box
/*		Open the Document
/*			Open the Page
/*				Install my font
/*				Print the Page
/*			Close the Page
/*		Until all pages are printed
/*		UnSpool the picture if Printing to an ImageWriter
/*																			 */
/* ************************************************************************* */

void doPrint()
{
	boolean			done;
	GrafPortPtr		myPort;
	int				refNum;
	FontInfoRecord	theInfoRec;
	PrStatusRec		myStatusRec;
	GrafPortPtr		NowPrintingDialogPtr;
	FontHndl		oldFont;

	/* Get some last minute information, and give user a chance to Cancel	*/

	if (PrJobDialog(PrintHndl)) {

		/* Got the OK. Show a nifty status box.								*/

		NowPrintingDialogPtr = GetNewModalDialog(&NowPrintingDialog);
		BeginUpdate(NowPrintingDialogPtr);
		DrawDialog(NowPrintingDialogPtr);
		EndUpdate(NowPrintingDialogPtr);

		/* See if we a printing to a LaserWriter.							*/

		PostScript = ((**PrintHndl).prInfo.iDev == 3);


		/* Set up our own logical page size. This lets me set some margin	*/
		/* space for 3-hole punches 									 	*/

 		(**PrintHndl).prInfo.rPage.v1 += (sideways?50:5);
		(**PrintHndl).prInfo.rPage.h1 += (sideways?5:50);
		(**PrintHndl).prInfo.rPage.v2 -= 5;
		(**PrintHndl).prInfo.rPage.h2 -= 5;

		oldFont = GetFont();
		InstallFont(theFont,0); /* do this once so we can get info on it	*/
		GetFontInfo(&theInfoRec);
		SetFont(oldFont);				/* Set us back to the System Font	*/
		lineHeight = theInfoRec.ascent + theInfoRec.descent + theInfoRec.leading;


		/* We need to kludge something here. Although we set up rPage		*/
		/* above, PrOpendoc and PrOpenPage reset it! So we make a local		*/
		/* copy here, and use IT in all of our calculations.				*/

		myPage = (**PrintHndl).prInfo.rPage;
		myPagePtr =  &myPage;


		/* Initialize the page number counter								*/
		pageNum = 1;


		/* Set up variables used to determine where to start and stop		*/
		/* printing on the page.											*/
		/*																	*/
		/* Step One: assume that there are no borders, headers, or			*/
		/* footers.															*/

		lineTop =  (*myPagePtr).v1;
		lineBottom = (*myPagePtr).v2;


		/* Step Two: See what types of borders we are using, and 			*/
		/* adjust the top and bottom margins accordingly.					*/

		if (myBorders[0].used) {
			if (myBorders[1].used)
				lineTop = (*myPagePtr).v1 + lineHeight;
			if (myBorders[2].used)
				lineTop = (*myPagePtr).v1 + lineHeight*3;
			if (myBorders[4].used)
				lineBottom = (*myPagePtr).v2 - lineHeight;
			if (myBorders[3].used)
				lineBottom = (*myPagePtr).v2 - lineHeight*3;
		}


		/* Step Three: If we are using any Headers or Footers, set			*/
		/* aside space for them.											*/

		GetIText(myDialog,Header,HeaderFooter);
		haveHeader = ((int)*HeaderFooter > 0);
		GetIText(myDialog,Footer,HeaderFooter);
		haveFooter = ((int)*HeaderFooter > 0);

		if (haveHeader) lineTop = (*myPagePtr).v1 + lineHeight*3;
		if (haveFooter) lineBottom = (*myPagePtr).v2 - lineHeight*3;


		/* Figure out how many lines that comes out to.						*/
		maxLines = (lineBottom-lineTop) / lineHeight;


		/* Attempt to open the file. If that succeeds, then go into the		*/
		/* Printing loop.													*/

		if (OpenFile() != 0) {

			BeginUpdate(myDialog);	/* update the window first				*/
			DrawDialog(myDialog);
			EndUpdate(myDialog);

			/* We now enter into the Classical Print Loop. Notice the		*/
			/* gyrations I go through in the middle with the checking of	*/
			/* PrError(). Normally, you wouldn't think that this would be	*/
			/* necessary, as the Print Manager will automatically skip over	*/
			/* the rest of the printing if an error occurs. However, I have	*/
			/* to make my routine more intelligent for 2 reasons:			*/
			/*		1) I can abort the reading of the rest of the file if	*/
			/*			I know that an error has occured. Even though the	*/
			/*			Print manager won't print anything if an error		*/
			/*			occurs, I can speed things up by doing this.		*/
			/*		2) The Print Manager short-circuits all of its routines	*/
			/*			when an error occurs. It makes a check of PrError()	*/
			/*			at the beginning of all routines and if it is not	*/
			/*			equal to zero, it returns before doing anything		*/
			/*			else. However, assume the case where the user		*/
			/*			presses Cmd-. before PrOpenPage() is called for the	*/
			/*			time. Among other things, PrOpenPage() is respon-	*/
			/*			sible for setting the port to the Printer Port		*/
			/*			('myPort' as listed below). If Cmd-. is pressed,	*/
			/*			PrOpenPage() will not do that, and the port is left	*/
			/*			set to my Dialog Window. If I were to then perform	*/
			/*			my InstallFont(), I would set the font of the		*/
			/*			Dialog Window, and not the Printer Port, like I		*/
			/*			wanted. So I have to make a check of PrError to see	*/
			/*			if it is OK to proceed.								*/

			myPort = PrOpenDoc(PrintHndl,NULL);	/* Start a print session	*/
			if (_toolErr != 0) PrSetError(_toolErr);
			do {
				PrOpenPage(myPort, NULL);	/* Open port for printing into.	*/
				if (_toolErr != 0) PrSetError(_toolErr);
				if (PrError() != 0) {		/* If an error occurs...		*/
					done = TRUE;			/* ... get outta here!			*/
				} else {					/* If no error occurs...		*/
					InstallFont(theFont,0); /* ...reset the font...			*/
					done = PrintAPage();	/* ...and print a page.			*/
				}
				PrClosePage(myPort);		/* Close the printer's GrafPort	*/
				if (_toolErr != 0) PrSetError(_toolErr);
			} while (!done);				/* "Bop til you drop!"			*/
			PrCloseDoc(myPort);				/* Close this print session.	*/
			if (_toolErr != 0) PrSetError(_toolErr);

			SetPort(myDialog);				/* Set port back to Dialog box.	*/
			CloseFile();					/* Close the file.				*/

			if (PrError() == 0) {			/* If no error occured...		*/
				if (MaxBlock() < 10240) {	/* Enough memory to de-spool?	*/
					PurgeAll(MyID);			/* No - try to get some.		*/
					if (MaxBlock() < 10240)	/* Enough memory now?			*/

						/* Not enough memory to de-spool to the ImageWriter	*/
						/* so show a dialog box saying so. However, that	*/
						/* begs the question of whether or not we have		*/
						/* enough memory to show this dialog box...			*/

						AlertWindow(0,NULL,NoMemAlertString);
						return;
				};
				PrPicFile(PrintHndl,NULL,&myStatusRec);
				CloseDialog(NowPrintingDialogPtr);	/* Remove status box.	*/

			} else {							/* If an error occurred...	*/

				CloseDialog(NowPrintingDialogPtr);	/* Remove status box.	*/
				ShowError(PrError());
			}
		}
	}
}


/* ************************************************************************* */
/*																			 */
/*	Draws just one part of the borders item. Called when part of the frame	 */
/*	has been clicked on, or when by DrawUserItem when the whole thing needs	 */
/*	to be updated.															 */
/*																			 */
/* ************************************************************************* */

void DrawLine(lineNum)
int	lineNum;
{
	/* If the whole control is disabled, or the specific part of it that we */
	/* are drawing is disabled, set the pen pattern to grey. Otherwise, set */
	/* it to black */

	if ((myBorders[lineNum].used) && (myBorders[0].used)) {
		PenNormal();
	} else {
		SetPenPat(greyPat);
	}
	PaintRect(&myBorders[lineNum].line);
}


/* ************************************************************************* */
/*																			 */
/*	This routine draws my Custom Items. It is passed a pointer to the 		 */
/*	Dialog box and the number of the item to draw. It uses this item in a	 */
/*	switch statement that draws all of the items. The Icon items are drawn	 */
/*	with PPToPort, and the borders control is drawn by looping through and	 */
/*	calling a routine that draws individual parts of the frame.				 */
/*																			 */
/* ************************************************************************* */

pascal void DrawUserItem(theDialog,item)
GrafPortPtr	theDialog;
int			item;
{
	int 		i,w;
	LongWord	oldFont;

	SetPort((GrafPortPtr)theDialog);
	switch (item) {
		case Borders:
			for (i = 1; i < 8; DrawLine(i++));
			PenNormal();
			break;
		case Portrait:
			PPToPort(&portraitLoc,
					 &portraitRect,
					 portraitButton.itemRect.h1,
					 portraitButton.itemRect.v1,
					 modeCopy);
			if (!sideways) InvertRect(&portraitButton.itemRect);
			break;
		case Landscape:
			PPToPort(&landscapeLoc,
					 &landscapeRect,
					 landscapeButton.itemRect.h1,
					 landscapeButton.itemRect.v1,
					 modeCopy);
			if (sideways) InvertRect(&landscapeButton.itemRect);
			break;
	}
}


/* ************************************************************************* */
/*																			 */
/*	Filter procedure for my main Dialog box. This routine looks for Key		 */
/*	Down events with the Command (Open-Apple) key down. If there is one, it	 */
/*	looks to see if the key pressed is one that corresponds to a button. If	 */
/*	so, then it tells the Dialog Manager that a hit occured on that button	 */
/*	and it flashes that button. It then tells the Dialog Manager that the	 */
/*	filter procedure handled the event, and that the Dialog Manager shouldnt */
/*	bother. If none of the above is true, then the Dialog Manager is told to */
/*	go ahead and handle the event.											 */
/*																			 */
/*	mod 6/27: Changes Carriage Returns into TABs							 */
/*																			 */
/* ************************************************************************* */

pascal Word DialogFilterProc(theDialog, theEvent, theItem)
GrafPortPtr		theDialog;
EventRecord		*theEvent;
long			*theItem;

{
	CtlRecHndl	tHandle;
	char		ch;
	int			myItem;


	if ((*theEvent).what == keyDownEvt) {	/* Is this a Keydown event?		*/
		ch = (*theEvent).message & 0x7f;	/* Yes, get the key into 'ch'	*/
		if (((appleKey & (*theEvent).modifiers) != 0)) {	/* Apple down?	*/
			/* Yes, see if the character pressed is special					*/
			myItem = 0;
			switch (ch) {
				case 'P':
				case 'p':
					myItem = Print;	/* cmd-P equivalent to pressing Print	*/
					break;
				case 'F':
				case 'f':
					myItem = ChFile;/* cmd-F equivalent to pressing File	*/
					break;
				case 'Q':
				case 'q':
					myItem = Quit;	/* cmd-Q equivalent to pressing Quit	*/
					break;
			}
			if (myItem != 0) {		/* Flash the button (if any)			*/
				tHandle = GetControlDItem(theDialog,myItem);
				HiliteControl(1,tHandle);
				HiliteControl(0,tHandle);
				*theItem = myItem;
				return(true);		/* Tell Dialog manager we did something	*/
			}
		} else {
			switch (ch) {
				case 0x0D:
					(*theEvent).message = 0x0009;	/* turn CRs into TABs	*/
					ch = 0x09;
				case 0x09:			/* update percent reduction if TAB hit	*/
					doPercent();
					break;
			}
		}
	}
	return(false);					/* Tell Dialog manager to handle it		*/
}



/* ************************************************************************* */
/*																			 */
/* EventLoop - This routine calls Modal Dialog to handle all events. It		 */
/* detects whether any items have been clicked on, and returns to us if any	 */
/* have. Note how we pass a pointer to a filter procedure. This procedure is */
/* called by the dialog manager to handle events. In my case, I use it to	 */
/* detect command key sequences, and to make them emulate clicks on certain	 */
/* buttons. I also set bit 31 of the pointer to the filter procedure to tell */
/* the dialog manager to perform its own filtering of events if I opt not to */
/* do any of my own. See page 6-66 of the ToolBox Reference Manual for a	 */
/* description of the standard filtering procedure.							 */
/*																			 */
/* ************************************************************************* */
EventLoop()
{
	word what;

	/* Call ModalDialog to handle events in a Dialog box I created in the	*/
	/* InitApp section.														*/

	do {
		what = ModalDialog((long)DialogFilterProc | 0x80000000);
		SetPort((GrafPortPtr)myDialog);
		if (what != Percent) doPercent();	/* do on any event BUT Percent	*/
		switch (what) {
			case About:				/* About button pressed */
				doAbout();
				break;
			case ChFile:			/* File button pressed */
				doChFile();
				break;
			case ChPrinter:			/* Choose Printer button pressed */
				doChPrinter();
				break;
			case ChFont:			/* Choose Font button pressed */
				doChFont();
				break;
			case PgSetup:			/* Page Setup button pressed */
				doPgSetup();
				break;
			case Print:				/* Print button pressed */
				doPrint();
				break;
			case UseBorder:			/* Use Border check box clicked on */
				doCheckBox();
				break;
			case Borders:			/* Borders User Item clicked on */
				doBorders();
				break;
			case Columns:			/* Event in Columns EditLine item */
				doColumns();
				break;
			case Landscape:			/* Click on Portrait or Landscape icons */
			case Portrait:
				doPrDirection(what);
				break;
		} /* end switch */
	} while (what != Quit);
}
